﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml.Serialization;
using System.Threading;

namespace Antioch.Base
{
    public interface InputMode
    {
        void Update(EventBasedInput Input, Jemgine.Engine.Simulation Sim);
    }

    public class NullInputMode : InputMode
    {
        public void Update(EventBasedInput Input, Jemgine.Engine.Simulation Sim) { }
    }

    public class GameStateModule : Jemgine.Engine.Module
	{
		public enum States
		{
			Running,
			Loading,
			Autosave,
			GameOver
		}

        private States _State;
        public States State { get { return _State; } set { _State = value;  StateDuration = 0.0f; } }
        public float StateDuration { get; private set; }

        public Jemgine.Data.EntityRecord Player { get; set; }

		public SaveableState ActiveSaveState;
        public SaveableState MapEnteredSaveState;
		public bool AutosaveEnabled = false;
		public Action<SaveableState> SaveAction;
        public SaveableState PreviousSavedGame = null;

        public void InitiateGame(SaveableState gameState, bool Autosave)
        {
            this.MapEnteredSaveState = new SaveableState(gameState);
            this.AutosaveEnabled = Autosave;

            this.BeginChangeMap(MapEnteredSaveState.MapName, MapEnteredSaveState.NodeToFire);
        }

        Stack<InputMode> ModeStack = new Stack<InputMode>();
        private InputMode _inputMode { get { return ModeStack.Peek(); } }
        public void PushInputMode(InputMode Mode) { ModeStack.Push(Mode); }
        public void PopInputMode() { ModeStack.Pop(); }

        public void Input(EventBasedInput Input)
        {
            if (ModeStack.Count > 0) _inputMode.Update(Input, Sim);
        }

        public override void BeginSimulation()
        {
            ModeStack.Clear();
            PushInputMode(new NullInputMode());
        }

		public Mutex UpdateLock = new Mutex();
        private bool PendingMapChange = false;

        public override void Update(float ElapsedSeconds)
        {
            StateDuration += ElapsedSeconds;
        }

    	public void BeginChangeMap(String newMapName, String NodeToFire)
		{
            if (PendingMapChange == true) return;
            PendingMapChange = true;
			System.Threading.Thread LoadingThread = new System.Threading.Thread(ChangeMapThreadFunc);

            if (ActiveSaveState == null) ActiveSaveState = new SaveableState(MapEnteredSaveState);
            ActiveSaveState.MapName = newMapName;
            ActiveSaveState.NodeToFire = NodeToFire;
            MapEnteredSaveState.CopyFrom(ActiveSaveState);

            UpdateLock.WaitOne(); //This can't enter until the update cycle ends.
            State = States.Loading;
			LoadingThread.Start();
            UpdateLock.ReleaseMutex();
		}

        void ChangeMapThreadFunc()
        {
            UpdateLock.WaitOne(); //This can't enter until the update cycle ends.
            State = States.Loading;
            PendingMapChange = false;

            Sim.EndSimulation();
            Sim.BeginSimulation();

            UpdateLock.ReleaseMutex();

            Player = null;

            Jemgine.Data.ScriptNode NodeToFire = null;
            Jemgine.Data.MapData Data = null;
            if (!Sim.ImportMap(out Data, ActiveSaveState.MapName, Vector2.Zero, true, (A) =>
                {
                    Sim.DefaultElementHandler(A);
                    if (A is Jemgine.Data.ScriptNode && (A as Jemgine.Data.ScriptNode).Name == ActiveSaveState.NodeToFire)
                        NodeToFire = A as Jemgine.Data.ScriptNode;
                }))
            {
                Sim.ImportMap(out Data, "MainMenu", Vector2.Zero, true, Sim.DefaultElementHandler);
            }

            System.GC.Collect();

            if (AutosaveEnabled)
            {
                State = States.Autosave;
                if (SaveAction != null) SaveAction(MapEnteredSaveState);
                PreviousSavedGame = new SaveableState(MapEnteredSaveState);
            }

            Jemgine.Engine.ScriptModule Script = Sim.GetModule<Jemgine.Engine.ScriptModule>();
            if (Script != null)
                Script.FireTerminal(new Jemgine.Data.InputTerminal { Me = NodeToFire, LocalID = 0 },
                    new Jemgine.Data.ScriptPayload { SenderRef = null, Physics = null }, 0);

            UpdateLock.WaitOne();
            State = States.Running;
            UpdateLock.ReleaseMutex();
        }

		public void ReloadCurrentChamber()
		{
            ActiveSaveState = null;
			BeginChangeMap(MapEnteredSaveState.MapName, MapEnteredSaveState.NodeToFire);
		}

        public void Gameover()
        {
            State = States.GameOver;
            ActiveSaveState = null;
        }

		
	}
}
